package com.hevery.beans.factory;

import com.hevery.beans.BeanReference;
import com.hevery.beans.BeanDefinition;
import com.hevery.beans.PropertyValue;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @description: 可实现自动装配的BeanFactory。
 * @author: hongyu.zhang
 * @create: 2018-03-20
 **/
public class AutowireCapableBeanFactory extends AbstractBeanFactory {
    @Override
    Object doCreateBean(BeanDefinition beanDefinition) throws Exception{
        Object bean = beanDefinition.getBeanClass().newInstance();
        beanDefinition.setBean(bean);
        applyPropertyValues(bean,beanDefinition);
        return bean;
    }

    private void applyPropertyValues(Object bean, BeanDefinition beanDefinition) throws Exception{
        for (PropertyValue propertyValue : beanDefinition.getPropertyValues().getPropertyValues()) {
            Object value = propertyValue.getValue();
            if(value instanceof BeanReference){
                //如果属性是ref而不是value类型就先实例化那个ref的bean，然后装载到这个value里
                BeanReference beanReference = (BeanReference)value;
                //先实例化ref的bean再装配进去---->把BeanReference实例化为对应的真正的bean对象，此时value的值已经改变
                /**
                 * 误区：之前一直以为此处的getBean(String name)返回为null,因为这是还是在doCreateBean(BeanDefinition beanDefinition)
                 * 阶段，还没有创建完，所以返回的为null。但是仔细梳理之后，才发现不是这样的，因为applyPropertyValues(Object bean, BeanDefinition beanDefinition)
                 * 实际是在注册具有<property>标签的BeanDefinition（需要ref的Bean），而ref(<bean id="outputService" class="com.hevery.service.OutputServiceImpl">)
                 * 实际上已经注册了，所以可以获取到。
                 */
                value = getBean(beanReference.getName());
            }
            /**
             * 例如：<bean id="dog" class="com.hevery.entity.Dog">
                        <property name="Id" value="101"></property>
                    </bean>
             先获取dog对象的setId方法，然后通过反射调用该方法将value设置进去
             getDeclaredMethod方法的第一个参数是方法名，第二个参数是该方法的参数列表
             */
            Method declaredMethod = null;
            try {
                declaredMethod = bean.getClass().getDeclaredMethod(convertSetMethod(propertyValue.getName()),value.getClass());
                declaredMethod.setAccessible(true);
                declaredMethod.invoke(bean,value);
            } catch (NoSuchMethodException e) {
                // 如果该bean没有setXXX的类似方法，就直接将value设置到相应的属性域内
                Field field = bean.getClass().getDeclaredField(propertyValue.getName());
                field.setAccessible(true);
                field.set(bean,value);
            }
        }
    }
    /**
     * 拼接Field对应的set方法
     * @param filed
     * @return
     */
    private String convertSetMethod(String filed){
        return "set"+filed.substring(0,1).toUpperCase() + filed.substring(1);
    }
}
